/**
 * Copyright (C) 2016-2019 DSpot Sp. z o.o
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.dspot.declex.api.action.processor;

import com.dspot.declex.api.action.process.ActionInfo;
import com.dspot.declex.api.action.process.ActionMethod;
import com.helger.jcodemodel.IJExpression;
import com.helger.jcodemodel.JBlock;
import com.helger.jcodemodel.JMethod;
import com.helger.jcodemodel.JVar;

import org.ohosannotations.annotations.EFraction;

import java.security.SecureRandom;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;

import static com.helger.jcodemodel.JExpr._null;
import static com.helger.jcodemodel.JExpr._this;
import static com.helger.jcodemodel.JExpr.lit;
import static com.helger.jcodemodel.JExpr.ref;

public class AbilityActionProcessor extends BaseActionProcessor {
    private SecureRandom secureRandom = new SecureRandom();
    public Map<Object, List<Integer>> requestCodes = new HashMap<>();

    @Override
    public void validate(ActionInfo actionInfo) {
        super.validate(actionInfo);

        ActionMethod init = getActionMethod("init");

        if (init.metaData == null) {
            return;
        }
    }

    @Override
    public void process(ActionInfo actionInfo) {
        super.process(actionInfo);

        IJExpression context = getMethodInHolder("getContextRef");
        if (getAnnotatedElement().getAnnotation(EFraction.class) != null) {
            context = _this();
        }
        if (context == _this()) {
            context = getGeneratedClass().staticRef("this");
        }
        addPostInitBlock(getAction().invoke("setBuilder").arg(context));

        for (ActionMethod withResult : actionInfo.methods.get("withResult")) {
            if (withResult.metaData != null) {
                JVar resultCode = getMethodInHolder("getOnAbilityResultResultCodeParam");
                JVar data = getMethodInHolder("getOnAbilityResultDataParam");

                JBlock block;
                if (withResult.params.size() == 0) {
                    int requestCode = getRandomCode();
                    addPreBuildBlock(getAction().invoke("withResult").arg(lit(requestCode)));

                    block = getMethodInHolder("getOnAbilityResultCaseBlock", null, requestCode);

                    block = block._if(getAction().neNull())._then();

                } else {
                    JMethod method = getMethodInHolder("getOnAbilityResultMethod");
                    block = method.body()._if(
                        getAction().neNull().cand(getAction().invoke("getRequestCode").eq(ref("requestCode")))
                    )._then();

                }

                block = block._if(getAction().invoke("getOnResult").neNull())._then();
                block.add(getAction().invoke("getOnResult").invoke("onAbilityResult")
                    .arg(getAction().invoke("getRequestCode")).arg(resultCode).arg(data)
                );
                block.assign(getAction(), _null());

                break;
            }
        }

    }

    private Integer getRandomCode() {
        List<Integer> codes = requestCodes.get(getHolder());
        if (codes == null) {
            codes = new LinkedList<>();
            requestCodes.put(getHolder(), codes);
        }

        //Generate the random code;
        Integer random;
        do {

            random = (secureRandom.nextInt() * 65536); //Only lower than 16bits request codes
        } while (codes.contains(random));

        codes.add(random);

        return random;
    }
}
